﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PublicEntity
{
    /// <summary>
    /// Socket封装byte[]的发送帮助类
    /// </summary>
    /// <remarks>
    /// 此类解释:
    /// byte[]从前往后数:
    /// 请求路径长度->请求路径
    /// 然后循环以下操作
    ///     (参数名称长度->参数名称->
    ///     参数类型长度(0表示普通参数,其他表示是文件)->文件类型(不是文件则跳过)->
    ///     文件名称长度(不是文件则跳过)->文件名称(不是文件则跳过)->
    ///     参数值长度的长度->参数值长度->参数值)
    /// 参数值长度组成解释:
    /// 1.参数值长度的长度表示参数值长度的位数
    /// 2.将参数值长度位数以内的值提取出来组成byte[]
    ///  2.1 假如 参数值长度位数 = 3 则提取后面三个byte字节
    ///      byte[] data = new byte[] { 101, 255, 15 };
    ///      var IEList = data.Select(x => x.ToString("x2").PadLeft(2, '0'));//先转16进制在补齐位数,因为byte最大值255表示ff所以只需要补齐2位即可
    ///      var str16 = string.Join("", IEList);
    ///      var num = Convert.ToInt64(str16, 16);//表示参数值长度
    /// </remarks>
    public class SocketByteSend
    {
        /// <summary>
        /// 实例化
        /// </summary>
        /// <param name="encoding">字符编码,默认Encoding.UTF8</param>
        public SocketByteSend(Encoding encoding = null)
        {
            this.encoding = encoding ?? Encoding.UTF8;
        }

        /// <summary>
        /// 字符编码
        /// </summary>
        public Encoding encoding { get; set; }

        /// <summary>
        /// 会被发送出去的byte集合
        /// </summary>
        public IEnumerable<byte> SendByteIEnumerable { get; private set; }

        /// <summary>
        /// 发送出去的byte[]
        /// </summary>
        /// <returns></returns>
        public byte[] SendByteArray()
        {
            return SendByteIEnumerable.ToArray();
        }

        /// <summary>
        /// 添加请求路径
        /// </summary>
        /// <param name="ApiClass">Socket服务端类型名称</param>
        /// <param name="ApiAction">Socket服务端方法</param>
        public void AddApiPath(string ApiClass, string ApiAction)
        {
            SendByteIEnumerable = Merge(SendByteIEnumerable, ApiPathToIEnumerable(ApiClass, ApiAction, encoding));
        }

        /// <summary>
        /// 添加请求参数
        /// </summary>
        /// <param name="Name">参数名</param>
        /// <param name="Value">参数值</param>
        public void AddValue(string Name, object Value)
        {
            SendByteIEnumerable = Merge(SendByteIEnumerable, ValueToIEnumerable(Name, Value, encoding));
        }

        /// <summary>
        /// 添加请求文件
        /// </summary>
        /// <param name="Name">参数名</param>
        /// <param name="FileName">文件名称</param>
        /// <param name="FileType">文件类型</param>
        /// <param name="ByteFile">文件</param>
        public void AddFile(string Name, string FileName, string FileType, byte[] ByteFile)
        {
            SendByteIEnumerable = Merge(SendByteIEnumerable, FileToIEnumerable(Name, FileName, FileType, ByteFile, encoding));
        }

        /// <summary>
        /// 添加请求文件
        /// </summary>
        /// <param name="Name">参数名</param>
        /// <param name="FileType">文件类型</param>
        /// <param name="FilePath">文件路径</param>
        public void AddFile(string Name, string FileType, string FilePath)
        {
            SendByteIEnumerable = Merge(SendByteIEnumerable, FileToIEnumerable(Name, FileType, FilePath, encoding));
        }

        /// <summary>
        /// 请求路径转Socket需要的byte
        /// </summary>
        /// <param name="ApiClass">Socket服务端类型名称</param>
        /// <param name="ApiAction">Socket服务端方法</param>
        /// <param name="encoding">字符编码</param>
        /// <returns></returns>
        public static IEnumerable<byte> ApiPathToIEnumerable(string ApiClass, string ApiAction, Encoding encoding = null)
        {
            encoding = encoding ?? System.Text.Encoding.UTF8;
            string ApiPath = ApiClass + "/" + ApiAction;
            var bytes = encoding.GetBytes(ApiPath);//请求路径 Byte
            yield return (byte)bytes.Length;//请求路径长度
            foreach (var @byte in bytes)
            {
                yield return @byte;//请求路径
            }
        }

        /// <summary>
        /// 参数转Socket需要的byte
        /// </summary>
        /// <param name="Name">参数名</param>
        /// <param name="Value">参数值</param>
        /// <param name="encoding">字符编码</param>
        /// <returns></returns>
        public static IEnumerable<byte> ValueToIEnumerable(string Name, object Value, Encoding encoding = null)
        {
            if (Value == null)
            {
                yield break;
            }
            encoding = encoding ?? System.Text.Encoding.UTF8;
            var bytes = encoding.GetBytes(Name);//参数名称 Byte
            yield return (byte)bytes.Length;//参数名称长度
            foreach (var @byte in bytes)
            {
                yield return @byte;//参数名称
            }
            yield return 0;//参数类型长度(0表示普通参数,其他表示是文件)
            bytes = encoding.GetBytes(Value.ToString());//参数值 Byte
            var lenghtArray = NumberToIEnumerable(bytes.LongLength).ToArray();//参数值长度 Byte
            yield return (byte)lenghtArray.Length;//参数值长度的长度
            foreach (var @byte in lenghtArray)
            {
                yield return @byte;//参数值长度
            }
            foreach (var @byte in bytes)
            {
                yield return @byte;//参数值
            }
        }

        /// <summary>
        /// 文件转Socket需要的byte
        /// </summary>
        /// <param name="Name">参数名</param>
        /// <param name="FileName">文件名称</param>
        /// <param name="FileType">文件类型</param>
        /// <param name="ByteFile">文件</param>
        /// <param name="encoding">字符编码</param>
        /// <returns></returns>
        public static IEnumerable<byte> FileToIEnumerable(string Name, string FileName, string FileType, byte[] ByteFile, Encoding encoding = null)
        {
            if (ByteFile == null)
            {
                yield break;
            }
            encoding = encoding ?? System.Text.Encoding.UTF8;

            var bytes = encoding.GetBytes(Name);//参数名称 Byte
            yield return (byte)bytes.Length;//参数名称长度
            foreach (var @byte in bytes)
            {
                yield return @byte;//参数名称
            }

            bytes = encoding.GetBytes(FileType);//文件类型 Byte
            yield return (byte)bytes.Length;//文件类型长度(0表示普通参数,其他表示是文件)
            foreach (var @byte in bytes)
            {
                yield return @byte;//文件类型
            }

            bytes = encoding.GetBytes(FileName);//文件名称 Byte
            yield return (byte)bytes.Length;//文件名称长度
            foreach (var @byte in bytes)
            {
                yield return @byte;//文件名称
            }

            bytes = ByteFile;//参数值 Byte
            var lenghtArray = NumberToIEnumerable(bytes.LongLength).ToArray();//参数值长度 Byte
            yield return (byte)lenghtArray.Length;//参数值长度的长度
            foreach (var @byte in lenghtArray)
            {
                yield return @byte;//参数值长度
            }
            foreach (var @byte in bytes)
            {
                yield return @byte;//参数值
            }
        }

        /// <summary>
        /// 文件转Socket需要的byte
        /// </summary>
        /// <param name="Name">参数名</param>
        /// <param name="FileType">文件类型</param>
        /// <param name="FilePath">文件路径</param>
        /// <param name="encoding">字符编码</param>
        /// <returns></returns>
        public static IEnumerable<byte> FileToIEnumerable(string Name, string FileType, string FilePath, Encoding encoding = null)
        {
            byte[] bytes = null;
            string FileName = null;
            if (System.IO.File.Exists(FilePath))
            {
                FileName = System.IO.Path.GetFileName(FilePath);
                bytes = System.IO.File.ReadAllBytes(FilePath);
            }
            return FileToIEnumerable(Name, FileName, FileType, bytes, encoding);
        }

        /// <summary>
        /// 数字转16进制后在转byte
        /// </summary>
        /// <param name="Number">数字</param>
        /// <returns></returns>
        public static IEnumerable<byte> NumberToIEnumerable(long Number)
        {
            var str16 = Convert.ToString(Number, 16);
            if (str16.Length % 2 != 0)
            {
                str16 = "0" + str16;
            }
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var ch in str16)
            {
                stringBuilder.Append(ch);
                if (stringBuilder.Length == 2)
                {
                    yield return Convert.ToByte(stringBuilder.ToString(), 16);
                    stringBuilder.Clear();
                }
            }
            if (stringBuilder.Length > 0)
            {
                yield return Convert.ToByte(stringBuilder.ToString(), 16);
            }
        }

        /// <summary>
        /// 合并两个IEnumerable集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        private static IEnumerable<T> Merge<T>(IEnumerable<T> first, IEnumerable<T> second)
        {
            if (first != null)
            {
                foreach (var item in first)
                {
                    yield return item;
                }
            }
            if (second != null)
            {
                foreach (var item in second)
                {
                    yield return item;
                }
            }
        }
    }
}
